package com.ezops.poc.parser;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ezops.poc.bo.AssetLookUp;
import com.ezops.poc.bo.ClientLookUp;
import com.ezops.poc.bo.ClientTradeModel;
import com.ezops.poc.bo.IClientModel;
import com.ezops.poc.util.EzopsConstants;
import com.ezops.poc.util.LoggerUtil;
import com.ezops.poc.util.ParseException;

/**
 * This is a custom converter class which will transform the data (in format @code
 * (List<List<String>>)) into the client format pojo
 * 
 * TODO : We can have a factory of these converters in future for converting the
 * data into different set of pojo or make a dynamic model.
 * 
 * @author vgaurav
 * 
 */

@Component(value = "clientFormatParser")
public class ClientFormatParser implements Processor {

	private static final Logger logger = LoggerFactory
			.getLogger(ClientFormatParser.class);

	@Autowired
	private AssetLookUp assetLookUp;

//	@Autowired
//	@Qualifier("processEngineConfiguration")
//	private ProcessEngineConfiguration config;
	@Autowired
	private ClientLookUp clientLookUp;

	// CLIENTNAME_ASSETTYPE_NAME.csv
	private final static Pattern FILENAME_PATTERN = Pattern
			.compile("(.*?)_(.*?)\\..*");

	/**
	 * Process the incoming data, parse and convert it into model and send it
	 * further down the pipe.
	 * 
	 * @throws ParseException
	 */

	public void process(Exchange exchange) throws ParseException {
		logger.trace("Parsing the incoming messgae and generating Client Model");
		
//		System.out.println("##### USER COUNT ######"
//				+ config.getIdentityService()
//						.createUserQuery().count());
//		System.out.println("##### USER COUNT ######"
//				+ config.getProcessEngineConfiguration().getIdentityService().createUserQuery().count());

		String fileName = (String) exchange.getIn().getHeader(
				Exchange.FILE_NAME_CONSUMED);

		Matcher m = FILENAME_PATTERN.matcher(fileName);
		if (!m.matches()) {
			throw new IllegalArgumentException(
					"Filename doesn't match expected format: " + fileName);
		}

		@SuppressWarnings({ "unchecked" })
		List<List<String>> body = (List<List<String>>) exchange.getIn()
				.getBody();
		String[] header = extractHeader(body);

		setHeaderInExchangeHeader(header, exchange.getIn());
		List<List<String>> data = extractData(body);

		try {
			// Set the parsed list of model into exchange body for further
			// processing.
			exchange.getIn().setBody(parse(header, data, fileName, m.group(1)));
		} catch (Exception e) {
			e.printStackTrace();
			throw new ParseException(fileName, e, "Couldn't parse the file- "
					+ fileName + ". Reason - " + e.getMessage());
		}

	}

	/**
	 * Set the file header in exchnage header for generating error reports.
	 * 
	 * @param header
	 * @param in
	 */
	private void setHeaderInExchangeHeader(String[] header, Message in) {

		in.setHeader(EzopsConstants.FILE_HEADER, header);
	}

	/**
	 * Set the data to be parsed. First row is considered as a header.
	 * 
	 * @param incomingData
	 * @return
	 */
	private List<List<String>> extractData(List<List<String>> incomingData) {

		if (incomingData == null || incomingData.isEmpty()) {
			throw new IllegalArgumentException("File is empty");
		}
		// remove header
		incomingData.remove(0);
		return incomingData;
	}

	/**
	 * Validate if the headers are correct. Will be implemented in future.
	 * 
	 * @param headers
	 * @return
	 */
	private boolean validateHeaders(List<String> headers) {
		if (headers.isEmpty())
			return false;
		return true;

	}

	/**
	 * Return the headers.
	 * 
	 * @param columns
	 */
	private String[] extractHeader(List<List<String>> incomingData) {
		if (incomingData == null || incomingData.size() == 0) {
			throw new IllegalArgumentException("Header can not be empty");
		}
		List<String> headers = incomingData.get(0);
		validateHeaders(headers);

		logger.debug("The client file has following header"
				+ LoggerUtil.getLoggableFormat(headers));
		return headers.toArray(new String[headers.size()]);
	}

	/**
	 * Parse the @param data into @param classType object using reflection.
	 * 
	 * @param classType
	 * @param header
	 * @param data
	 * @param fileName
	 * @param assetId
	 * @param clientId
	 * @return
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	private List<IClientModel> parse(String[] header, List<List<String>> data,
			String fileName, String clientId) throws NoSuchFieldException,
			SecurityException, IllegalArgumentException,
			IllegalAccessException, InstantiationException {

		List<IClientModel> convertedList = new ArrayList<IClientModel>();

		for (List<String> row : data) {
			IClientModel instance = new ClientTradeModel();

			Iterator<String> dataIterator = row.iterator();

			// transform row into pojo.
			for (String field : header) {

				switch (field) {
				case "Asset Class":
					int assetId = assetLookUp.lookUp(dataIterator.next());
					instance.setAssetId(assetId);
					// for sending this o/p file
					instance.addField(field, String.valueOf(assetId));

					break;
				case "Trade ID":
					instance.setClientTradeId(Integer.parseInt(dataIterator
							.next()));
					break;
				default:
					instance.addField(field, dataIterator.next());

				}
				// String pojoField = Character.toLowerCase(field.charAt(0))
				// + field.substring(1, field.length());
				//
				// pojoField = pojoField.replace(".", "").replace(" ", "");
				// Field reflectiveField =
				// classType.getDeclaredField(pojoField);
				// reflectiveField.setAccessible(true);
				// reflectiveField.set(instance, dataIterator.next());
			}

			instance.setFileName(fileName);
			instance.setClientId(clientLookUp.lookUp(clientId));

			convertedList.add(instance);
		}
		logger.debug("Total number of trade recods in the file -" + fileName
				+ " is - " + convertedList.size());
		return convertedList;
	}
	
}
